#ifndef __RINGBUFFER_H
#define __RINGBUFFER_H

#define rt_inline	  static __inline
#define RT_NULL		  ( 0 )
#define RT_ALIGN_SIZE ( 4 )
#define RTM_EXPORT( symbol )
#define RT_ASSERT( EX )
#define RT_ALIGN_DOWN( size, align ) ( ( size ) & ~( ( align )-1 ) )

/* RT-Thread basic data type definitions */
typedef signed char	   rt_int8_t;	/**<  8bit integer type */
typedef signed short   rt_int16_t;	/**< 16bit integer type */
typedef signed long	   rt_int32_t;	/**< 32bit integer type */
typedef unsigned char  rt_uint8_t;	/**<  8bit unsigned integer type */
typedef unsigned short rt_uint16_t; /**< 16bit unsigned integer type */
typedef unsigned long  rt_uint32_t; /**< 32bit unsigned integer type */
typedef int			   rt_bool_t;	/**< boolean type */

/* 32bit CPU */
typedef long		  rt_base_t;  /**< Nbit CPU related date type */
typedef unsigned long rt_ubase_t; /**< Nbit unsigned CPU related data type */

typedef rt_base_t  rt_err_t;  /**< Type for error number */
typedef rt_ubase_t rt_size_t; /**< Type for size number */

/* ring buffer */
struct rt_ringbuffer {
	rt_uint8_t* buffer_ptr;
	/* use the msb of the {read,write}_index as mirror bit. You can see this as
	 * if the buffer adds a virtual mirror and the pointers point either to the
	 * normal or to the mirrored buffer. If the write_index has the same value
	 * with the read_index, but in a different mirror, the buffer is full.
	 * While if the write_index and the read_index are the same and within the
	 * same mirror, the buffer is empty. The ASCII art of the ringbuffer is:
	 *
	 *          mirror = 0                    mirror = 1
	 * +---+---+---+---+---+---+---+|+~~~+~~~+~~~+~~~+~~~+~~~+~~~+
	 * | 0 | 1 | 2 | 3 | 4 | 5 | 6 ||| 0 | 1 | 2 | 3 | 4 | 5 | 6 | Full
	 * +---+---+---+---+---+---+---+|+~~~+~~~+~~~+~~~+~~~+~~~+~~~+
	 *  read_idx-^                   write_idx-^
	 *
	 * +---+---+---+---+---+---+---+|+~~~+~~~+~~~+~~~+~~~+~~~+~~~+
	 * | 0 | 1 | 2 | 3 | 4 | 5 | 6 ||| 0 | 1 | 2 | 3 | 4 | 5 | 6 | Empty
	 * +---+---+---+---+---+---+---+|+~~~+~~~+~~~+~~~+~~~+~~~+~~~+
	 * read_idx-^ ^-write_idx
	 *
	 * The tradeoff is we could only use 32KiB of buffer for 16 bit of index.
	 * But it should be enough for most of the cases.
	 *
	 * Ref: http://en.wikipedia.org/wiki/Circular_buffer#Mirroring */
	rt_uint16_t read_mirror : 1;
	rt_uint16_t read_index : 15;
	rt_uint16_t write_mirror : 1;
	rt_uint16_t write_index : 15;
	/* as we use msb of index as mirror bit, the size should be signed and
	 * could only be positive. */
	rt_int16_t buffer_size;
};

/**
 * RingBuffer for DeviceDriver
 *
 * Please note that the ring buffer implementation of RT-Thread
 * has no thread wait or resume feature.
 */
void	  rt_ringbuffer_init( struct rt_ringbuffer* rb, rt_uint8_t* pool, rt_int16_t size );
rt_size_t rt_ringbuffer_put( struct rt_ringbuffer* rb, const rt_uint8_t* ptr, rt_uint16_t length );
rt_size_t rt_ringbuffer_put_force( struct rt_ringbuffer* rb, const rt_uint8_t* ptr, rt_uint16_t length );
rt_size_t rt_ringbuffer_putchar( struct rt_ringbuffer* rb, const rt_uint8_t ch );
rt_size_t rt_ringbuffer_putchar_force( struct rt_ringbuffer* rb, const rt_uint8_t ch );
rt_size_t rt_ringbuffer_get( struct rt_ringbuffer* rb, rt_uint8_t* ptr, rt_uint16_t length );
rt_size_t rt_ringbuffer_getchar( struct rt_ringbuffer* rb, rt_uint8_t* ch );

enum rt_ringbuffer_state {
	RT_RINGBUFFER_EMPTY,
	RT_RINGBUFFER_FULL,
	/* half full is neither full nor empty */
	RT_RINGBUFFER_HALFFULL,
};

rt_inline rt_uint16_t rt_ringbuffer_get_size( struct rt_ringbuffer* rb ) {
	RT_ASSERT( rb != RT_NULL );
	return rb->buffer_size;
}

rt_inline enum rt_ringbuffer_state rt_ringbuffer_status( struct rt_ringbuffer* rb ) {
	if ( rb->read_index == rb->write_index ) {
		if ( rb->read_mirror == rb->write_mirror )
			return RT_RINGBUFFER_EMPTY;
		else
			return RT_RINGBUFFER_FULL;
	}
	return RT_RINGBUFFER_HALFFULL;
}

/** return the size of data in rb */
rt_inline rt_uint16_t rt_ringbuffer_data_len( struct rt_ringbuffer* rb ) {
	switch ( rt_ringbuffer_status( rb ) ) {
	case RT_RINGBUFFER_EMPTY:
		return 0;
	case RT_RINGBUFFER_FULL:
		return rb->buffer_size;
	case RT_RINGBUFFER_HALFFULL:
	default:
		if ( rb->write_index > rb->read_index )
			return rb->write_index - rb->read_index;
		else
			return rb->buffer_size - ( rb->read_index - rb->write_index );
	};
}

/** return the size of empty space in rb */
#define rt_ringbuffer_space_len( rb ) ( ( rb )->buffer_size - rt_ringbuffer_data_len( rb ) )

#endif	// !__RINGBUFFER_H
